﻿using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;

namespace LINQPad.OpenAccess.Tests.Util
{
    /// <summary>
    /// Just a dirty hack used to resolve assemblies that are embedded within linqpad.
    /// Otherwise, we can't use affected properties within the unit tests.
    /// </summary>
    internal sealed class LINQPadEmbeddedAssemblyResolver : IDisposable
    {
        private readonly Assembly _linqPadAssembly;

        private string GetResourceKey( string assemblyToResolve )
        {
            return String.Format( "LINQPad.assemblies.{0}.bin", new AssemblyName( assemblyToResolve ).Name );
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="LINQPadEmbeddedAssemblyResolver" /> class.
        /// </summary>
        /// <param name="linqPadAssembly">The linq pad assembly.</param>
        public LINQPadEmbeddedAssemblyResolver( )
        {
            this._linqPadAssembly = typeof( LINQPad.Extensions ).Assembly;
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        }

        Assembly CurrentDomain_AssemblyResolve( object sender, ResolveEventArgs args )
        {
            var key = this.GetResourceKey( args.Name );
            var name = this._linqPadAssembly.GetManifestResourceInfo( key );
            if ( name != null )
            {
                // linpad embedds the assemblies as zipped binary.
                using ( var rs = this._linqPadAssembly.GetManifestResourceStream( key ) )
                using ( DeflateStream ds = new DeflateStream( rs, CompressionMode.Decompress ) )
                using ( var ms = new MemoryStream( ) )
                {
                    ds.CopyTo( ms );
                    return Assembly.Load( ms.ToArray( ) );
                }
            }
            return null;
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose( )
        {
            if ( AppDomain.CurrentDomain != null )
                AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
            GC.SuppressFinalize( this );
        }

        #endregion
    }
}
